#!/usr/bin/env perl
# The above shebang is for "perlbrew", otherwise use /usr/bin/perl or the file path quoted for "which perl"
#
# Please refer to the Plain Old Documentation (POD) at the end of this Perl Script for further information

use strict;
use Carp;
use Pod::Usage;
use JSON;
use WWW::Mechanize;
use Getopt::Long;
use Data::Dumper;

# #CONFIGURATION Remove "#" for Smart::Comments
# use Smart::Comments;

my $VERSION = "0.0.4"; # May be required to upload script to CPAN i.e. http://www.cpan.org/scripts/submitting.html

# Bing Search API - Required Parameters
#
# TODO Move the scalars below to a configuration file as per the recommendation(s) specified within Perl Best Practices
# AppId i.e. http://www.bing.com/toolbox/bingdeveloper/
my $bing_request_apiid = "9F3E29142254AAE98E9D8E466CCA27880D3B50B2";

# #CONFIGURATION Version, default value is "2.1" i.e. http://msdn.microsoft.com/en-us/library/dd250880.aspx
my $bing_request_version = "2.1";

# Sources i.e. http://msdn.microsoft.com/en-us/library/dd250895.aspx
my $bing_request_source = "Web";

# Bing Search API - Optional Parameters
#
# TODO Integrate into $bing_json_url
# #CONFIGURATION Market i.e. http://msdn.microsoft.com/en-us/library/dd251064.aspx
my $bing_request_market = "en-AU";

# TODO Integrate into $bing_json_url
# #CONFIGURATION Adult Enumeration i.e. http://msdn.microsoft.com/en-us/library/dd251007.aspx
my $bing_request_adult = "Off";

# Count i.e. http://msdn.microsoft.com/en-us/library/dd251004.aspx
my $bing_request_count = "50";

# Command line arguments for the Bing Search API
# Query i.e. http://msdn.microsoft.com/en-us/library/dd250887.aspx
my $bing_request_query;

# Offset i.e. http://msdn.microsoft.com/en-us/library/dd250920.aspx
my $bing_request_offset = "0";

# Command line arguments from Maltego
my $maltego_selected_entity_value = $ARGV[0];

# "###" is for Smart::Comments CPAN Module
### \$maltego_selected_entity_value is: $maltego_selected_entity_value;
my $ipaddr = $maltego_selected_entity_value;

# #CONFIGURATION "$maltego = 1"  is required for Maltego Local Transform
# See for further information http://ctas.paterva.com/view/Specification#Input_.28Maltego_GUI_-.3E__external_program.29
my $maltego = 0;

# Command line arguements

# Command line meta-options
my $usage;
my $man;

# TODO "update" disabled, refer to #Team_Explorer below
# my $update;

# TODO Display -usage if command line argument(s) are incorrect
GetOptions(
    "ip=s" => \$ipaddr,

# Command line meta-options
# -version is excluded as it is printed prior to processing the command line arguments
# TODO -verbose
    "usage" => \$usage,
    "man"   => \$man,

    # TODO "update" disabled, refer to #Team_Explorer below
    # "update"  => \$update
);

if ( $maltego == 1 ) {
    if ( $ipaddr !~ m/^\d/ ) {

        # TODO Term::ANSIColor
        print STDERR (
"ERROR: Configured for XML Output for Maltego i.e. \$maltego should be \"0\" not \"1\"\n"
        );
        exit();
    }
}

if ( ( $usage eq 1 ) or ( $man eq 1 ) ) {
    pod2usage( -verbose => 2 );
    die();
}

# TODO #TEAM_EXPLORER - Port from Subversion to Team Explorer
# if ($update eq 1) {
#    print "Please execute \"svn update\" from the command line\n";
#    die();
#}
if ( $maltego == 0 ) {
    print "\n\"Just The IP\" Alpha v$VERSION\n";
    print "\n";
    print "Copyright 2012 Christian Heinrich\n";
    print "Licensed under the Apache License, Version 2.0\n\n";
}
else {
    print("<MaltegoMessage>\n");
    print("<MaltegoTransformResponseMessage>\n");
    print("\t<UIMessages>\n");
    print(
"\t\t<UIMessage MessageType=\"Inform\">JusTIP Local Transform v$VERSION</UIMessage>\n"
    );
    print("\t</UIMessages>\n");
}

# Process the command line arguments
my $bing_request_query = "ip:" . $ipaddr;
chomp($bing_request_query);

# Directory/Folder for results of JusTIP
my $dir = "JusTIP";
if ( $maltego == 0 ) {
    if ( !( -e $dir ) ) {
        print STDERR ("Creating ./$dir\n\n");
        mkdir("./$dir");
    }
}

# Create a new Bing search JSON request
# TODO Refactor as a subroutine i.e. sub Bing_Search
# TODO Refactor with the Bing::Search CPAN Module
# TODO Support XML of Bing API v2.0 i.e. http://api.bing.com/xml.aspx i.e. http://msdn.microsoft.com/en-us/library/dd250893.aspx as an alternative to JSON and SOAP
# TODO Support SOAP of Bing API v2.0 as an alternative to JSON and XML
my $bing_json_url =
"http://api.bing.net/json.aspx?Appid=$bing_request_apiid&query=$bing_request_query&sources=$bing_request_source&Web.Count=$bing_request_count&Web.Offset=$bing_request_offset";

# "###" is for Smart::Comments CPAN Module
### \$bing_json_url is: $bing_json_url

# TODO Replace WWW::Mechanize with HTTP::Tiny CPAN Module
# TODO Replace WWW::Mechanize with libwhisker
# TODO Replace WWW::Mechanize with LWP::UserAgent
my $bing_http_request = WWW::Mechanize->new;

# TODO Availability of $bing_json_url i.e. is "up" and resulting HTTP Status Code
my $bing_http_response = $bing_http_request->get("$bing_json_url")->content;

# decode_json returns a reference to a hash
# TODO -debug flag as a command line argument
if ( $maltego == 0 ) {
    open( DEBUG_LOG, ">>./$dir/json_debug_log.txt" );
    print DEBUG_LOG "# \$bing_json_url\n\n";
    print DEBUG_LOG "$bing_json_url\n\n";
    print DEBUG_LOG "# \$bing_http_request->get(\$bing_json_url)->content\n\n";
    print DEBUG_LOG (
        Data::Dumper::Dumper( decode_json($bing_http_response) ) );
}

# WebReponse.Total i.e. http://msdn.microsoft.com/en-us/library/dd251004.aspx
my $bing_json_searchresponse_web_ref =
  decode_json($bing_http_response)->{SearchResponse}->{Web};
my %bing_response_json_web = %$bing_json_searchresponse_web_ref;
my $bing_response_total    = $bing_response_json_web{Total};

# "###" is for Smart::Comments CPAN Module
### \$bing_response_total is: $bing_response_total

if ( $bing_response_total == 0 ) {
    if ( $maltego == 0 ) {
        print("\t<Entities>\n");
        print("\t</Entities>\n");

        # http://ctas.paterva.com/view/Specification#Message_Wrapper
        print("</MaltegoTransformResponseMessage>\n");
        print("</MaltegoMessage>\n");
        exit;
    }
}

if ( $maltego == 0 ) {
    print DEBUG_LOG (
        "# decode_json(\$bing_http_response)->{SearchResponse}->{Web}\n\n");
    print DEBUG_LOG ( Data::Dumper::Dumper(%bing_response_json_web) );
}
my $bing_json_searchresponse_web_results_ref =
  decode_json($bing_http_response)->{SearchResponse}->{Web}->{Results};
my $bing_json_searchresponse_web_ref =
  decode_json($bing_http_response)->{SearchResponse}->{Web};
my @bing_response_json_web_results = @$bing_json_searchresponse_web_results_ref;
if ( $maltego == 0 ) {
    print DEBUG_LOG (
"# decode_json(\$bing_http_response)->{SearchResponse}->{Web}->{Results}\n\n"
    );
    print DEBUG_LOG ( Data::Dumper::Dumper(@bing_response_json_web_results) );
    close DEBUG_LOG;

   # TODO Highlight in red the creation of json_debug_log.txt in Term::ANSIColor

    # "###" is for Smart::Comments CPAN Module
    ### json_debug_log.txt is generated within: $dir
}

# The URL corresponding to the Search Result .html file is listed in this .CSV file
if ( $maltego == 0 ) {
    open( IPADDR, ">>./$dir/$ipaddr.csv" );
}

# Process the Bing search results.
# TODO Refactor parsing of Bing search results as a subroutine
my $bing_search_number_of_results = scalar(@bing_response_json_web_results);
if ( $maltego == 1 ) {
    print("\t<Entities>\n");
}
for (
    my $bing_search_result_number = 0 ;
    $bing_search_result_number < $bing_search_number_of_results ;
    $bing_search_result_number++
  )
{
    my $URL =
      $bing_response_json_web_results[$bing_search_result_number]{'Url'} . "\n";
    chomp($URL);
    $URL =~ m|(\w+)://([^/:]+)(:\d+)?/(.*)|;
    my $protocol    = $1;
    my $domain_name = $2;
    my $URI         = ( "/" . $4 );
    my $TCP_port;
    if ( $maltego == 0 ) {
        print( ( $bing_search_result_number + 1 ) . ". $URL\n" );
    }
    else {

        # http://ctas.paterva.com/view/Specification#Web_site

        print(
"\t\t<Entity Type=\"Website\"><Value>$domain_name</Value></Entity>\n"
        );
    }
    if ( $maltego == 0 ) {
        print IPADDR "$URL\n";
    }
}

# "###" is for Smart::Comments CPAN Module
### \$bing_request_count is: $bing_request_count
### \$bing_response_total is: $bing_response_total
if ( $bing_response_total > $bing_request_count ) {
    $bing_request_offset = ( $bing_request_offset + $bing_request_count );
    for (
        ;
        $bing_response_total > $bing_request_offset ;
        $bing_request_offset = ( $bing_request_offset + $bing_request_count )
      )
    {

        my $bing_request_query = "ip:" . $ipaddr;
        chomp($bing_request_query);

        # "###" is for Smart::Comments CPAN Module
        ### \$bing_request_offset is: $bing_request_offset
        # Create a new Bing search JSON request
        # TODO Refactor as a subroutine i.e. sub Bing_Search
        my $bing_http_request = WWW::Mechanize->new;

# TODO Availability of $bing_json_url i.e. is "up" and resulting HTTP Status Code
        my $bing_json_url =
"http://api.bing.net/json.aspx?Appid=$bing_request_apiid&query=$bing_request_query&sources=$bing_request_source&Web.Count=$bing_request_count&Web.Offset=$bing_request_offset";

        # "###" is for Smart::Comments CPAN Module
        ### \$bing_json_url is: $bing_json_url
        my $bing_http_response =
          $bing_http_request->get("$bing_json_url")->content;

        # decode_json returns a reference to a hash
        open( DEBUG_LOG, ">>./json_debug_log.txt" );
        if ( $maltego == 0 ) {
            print DEBUG_LOG ("# \$bing_json_url\n\n");
            print DEBUG_LOG ("$bing_json_url\n\n");
            print DEBUG_LOG (
                "# \$bing_http_request->get(\$bing_json_url)->content\n\n");
            print DEBUG_LOG (
                Data::Dumper::Dumper( decode_json($bing_http_response) ) );
            print DEBUG_LOG (
"# decode_json(\$bing_http_response)->{SearchResponse}->{Web}\n\n"
            );
            print DEBUG_LOG ( Data::Dumper::Dumper(%bing_response_json_web) );
        }
        my $bing_json_searchresponse_web_results_ref =
          decode_json($bing_http_response)->{SearchResponse}->{Web}->{Results};
        my $bing_json_searchresponse_web_ref =
          decode_json($bing_http_response)->{SearchResponse}->{Web};
        my @bing_response_json_web_results =
          @$bing_json_searchresponse_web_results_ref;
        if ( $maltego == 0 ) {
            print DEBUG_LOG
"# decode_json(\$bing_http_response)->{SearchResponse}->{Web}->{Results}\n\n";
            print DEBUG_LOG (
                Data::Dumper::Dumper(@bing_response_json_web_results) );
        }
        close DEBUG_LOG;

        if ( $maltego == 0 ) {

# The URL corresponding to the Search Result .html file is listed in this .CSV file
            open( IPADDR, ">>./$dir/$ipaddr.csv" );
        }

        # Loop through the search results.
        # TODO Refactor parsing of Bing search results as a subroutine
        $bing_search_number_of_results =
          scalar(@bing_response_json_web_results);
        for (
            my $bing_search_result_number = $bing_request_offset ;
            $bing_search_result_number <
            ( $bing_search_number_of_results + $bing_request_offset ) ;
            $bing_search_result_number++
          )
        {

            # "###" is for Smart::Comments CPAN Module
            ### \$bing_search_result_number is: $bing_search_result_number;
            my $URL =
              $bing_response_json_web_results[ $bing_search_result_number -
              $bing_request_offset ]{'Url'}
              . "\n";

            chomp($URL);
            my $URL =
              $bing_response_json_web_results[$bing_search_result_number]{'Url'}
              . "\n";
            chomp($URL);
            $URL =~ m|(\w+)://([^/:]+)(:\d+)?/(.*)|;
            my $protocol    = $1;
            my $domain_name = $2;
            my $URI         = ( "/" . $4 );
            my $TCP_port;

            if ( $maltego == 0 ) {
                print( ( $bing_search_result_number + 1 ) . ". $URL\n" );
            }
            else {
                print("\t<Entities>\n");
                print(
"\t\t<Entity Type=\"Website\"><Value>$domain_name</Value></Entity>\n"
                );
            }
        }
        if ( $maltego == 0 ) {
            close IPADDR;
        }

        # TODO Print that output has been written to ">>./$dir/$ipaddr.csv".
    }
}

if ( $maltego == 1 ) {
    print("\t</Entities>\n");

    # http://ctas.paterva.com/view/Specification#Message_Wrapper
    print("</MaltegoTransformResponseMessage>\n");
    print("</MaltegoMessage>\n");
}

=head1 NAME

justip.pl - "Just TIP"

=head1 VERSION

This documentation refers to justip $VERSION

=head1 CONFIGURATION

Set the value(s) marked as #CONFIGURATION above this POD
    
=head1 USAGE

justip.pl [-ip] [IP Address]

=head1 REQUIRED ARGUEMENTS

=head2 Command Line

-ip IP Address

=head2 Maltego

IPv4 Address Entity
                
=head1 OPTIONAL ARGUEMENTS

-man       Displays POD and exits.
-usage     Displays POD and exits.
-update    Displays the Team Explorer command to retrieve the latest update from codeplex.com

=head1 DESCRIPTION

"Just The IP" leverages the Bing API v2 and the ip: Bing Advanced Operator to list the other web sites hosted at this IP Address (that are indexed by Bing).

=head1 DEPENDENCIES

=head1 PREREQUISITES

JSON CPAN Module
WWW::Mechanize CPAN Module

=head1 COREQUISITES

=head1 INSTALLATION

Create the ./ipaddr directory/folder

=head1 OSNAMES

osx

=head1 SCRIPT CATEGORIES

Web

=head1 INCOMPATIBILITIES

=head1 BUGS AND LIMITATIONS

Please refer to the comments beginning with "TODO" in the Perl Code.

=head1 AUTHOR

Christian Heinrich

=head1 CONTACT INFORMATION

http://cmlh.id.au/contact

=head1 MAILING LIST

=head1 REPOSITORY

http://justip.codeplex.com/

=head1 FURTHER INFORMATION AND UPDATES

http://del.icio.us/cmlh/justip

=head1 LICENSE AND COPYRIGHT

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. 

Copyright 2012 Christian Heinrich
